import * as Chef from './worker.chef.js';
import Point from '../src/utils/Point.js';


let contoursi = 0;
let timer = null;
let resources = {};
let refreshTypes = {}; // refresh after websocket update
let reQueueTypes = {}; // refresh after queue

let connection = null,
    connectTime = Date.now(),
    serverTime = Date.now(),
    lastServerTime = Date.now(),
    lastMessageTime = Date.now(),
    workTime = Date.now();

// =================== initLocalDate
let virtualLifecycleTimer = null,
    virtualLifecycleDelay = 100,
    virtualVehiclesStep = 100;

let isRandomStatusEnable = undefined,
    lastRandomTime = Date.now(),
    randomLimit = 3000;

function initLocalDate({ virtualVehicles = 0, res, randomStatusEnable }) {
    isRandomStatusEnable = randomStatusEnable;
    // console.warn('[offline] initLocalDate: ', ...arguments);

    if (virtualLifecycleTimer) {
        // handleTerminate();
        // setTimeout(() => initLocalDate({ vehiclesLimit, offline }), 100);
        clearTimeout(virtualLifecycleTimer);
        resources = {};
        connectTime = Date.now();
        serverTime = Date.now();
        workTime = Date.now();
        // initLocalDate({ vehiclesLimit, offline });
        // return;
    }
    // handleTerminate();
    // console.warn('[kernel] initLocalDate...', virtualVehicles, res);
    resources = res;
    Chef.updateResources(resources);

    (resources['contours'] || []).forEach((c, i) => {
        Chef.contourCreate(i);
    });
    (resources['ranges'] || []).forEach((c, i) => {
        Chef.rangeCreate(i);
    });
    (resources['paths'] || []).forEach((c, i) => {
        Chef.pathCreate(i);
    });
    // console.warn('[kernel] resources: ', resources);

    if (!resources['vehicles']) resources['vehicles'] = [];
    refreshTypes = { contours: true, sements: true };
    postMessage({ task: 'resources', data: resources, refreshTypes }); // 全量

    for (let i = 0; i < virtualVehicles; i++) {
        let path = getRandomPath(),
            mod = getRandomModel();
        if (!path || !mod) {
            continue;
        }
        // let controlPoints = JSON.parse(JSON.stringify(path.controlPoints)),
        let controlPoints = path.controlPoints,
            rp = controlPoints.length && controlPoints[0];
        // console.error('rp: ', rp, rp.x, rp.y);
        let t = {
            // id: i,
            // id: resources['models'][midx]['vehicleKey'] + i,
            id: mod['vehicleKey'] + i,
            idx: i,
            isOnline: true,
            isValidated: true,
            remoteId: 'FREE',
            alertColor: [],
            controlPoints,
            tunedPos: {
                x: rp.x,
                y: rp.y,
                h: 0
            },
            route: [path.id],
            routeExt: path,
            properties: {}
        };
        // random alarm!
        if (randomStatusEnable) {
            let r = parseInt(Math.random() * 3);
            if (r === 1) t.updateIndex = 1;
            else if (r === 2) t.isOnline = false;
            t.properties.actionStatus = r;
        }

        resources['vehicles'].push(t);
        let idx = resources['vehicles'].length - 1;
        Chef.updateResources(resources);
        // Chef.vehicleCreate(t, serverTime, workTime);
        Chef.vehicleCreate(i, serverTime, workTime);
    }
    // console.info('[kernel.worker] resources: ', resources, this);
    serverTime = Date.now();
    // virtualLifecycleTimer = requestAnimationFrame(virtualLifecycle);
    virtualLifecycleTimer = setTimeout(virtualLifecycle, virtualLifecycleDelay);
}

function getRandomModel() {
    let midx = Math.floor(Math.random() * resources['models'].length);
    return resources['models'][midx];
}
function getRandomPath() {
    // let paths = getUsefulPaths(true),
    let paths = getUsefulPaths(),
    ridx = Math.floor(Math.random() * paths.length),
    path = paths[ridx];
    if (!path) return null;
    return JSON.parse(JSON.stringify(path));
}
function getUsefulPaths(straightOnly = false) {
    //eslint-disable-line
    if (!resources || !resources['paths']) return [];
    if (straightOnly) return resources['paths'].filter(s => s.controlPoints.length === 2 && s.length >= 50000);
    else return resources['paths'].filter(s => s.length > virtualVehiclesStep * 5);
    return resources['paths'];
}
function isTwoPointEquals(p1, p2) {
    //eslint-disable-line
    return !(p1.x !== p2.x || p1.y !== p2.y);
}
function getNextPoint(vehicle) {
    //eslint-disable-line
    let nextPoint = (vehicle.controlPoints && vehicle.controlPoints[1]) || { x: 0, y: 0 };
    let dic = 0,
        x = vehicle.tunedPos.x,
        y = vehicle.tunedPos.y,
        h = 0,
        dis = Math.sqrt(Math.pow(Math.abs(nextPoint.x - x), 2) + Math.pow(Math.abs(nextPoint.y - y), 2));
    if (nextPoint.x > x) dic = 1;
    else if (nextPoint.x < x) dic = 3;
    else if (nextPoint.y > y) dic = 0;
    else if (nextPoint.y < y) dic = 2;

    // // 斜线校验，会错位，闪。。。
    // if ((vehicle.dicExtTmp === 1 && dic === 3) || (vehicle.dicExtTmp === 0 && dic === 2)) {
    //     x = nextPoint.x;
    //     y = nextPoint.y;
    //     let path = getNextPath(vehicle.routeExt);
    //     if (path) {
    //         // console.warn('[kernel] getVirtualPosition: ', path.id);
    //         vehicle.controlPoints = path.controlPoints;
    //         vehicle.routeExt = path;
    //         vehicle.route = [path.id];
    //         return { x, y, h };
    //     }
    // }
    // vehicle.dicExtTmp = dic;
    //
    // if (isNaN(dis) || dis <= virtualVehiclesStep * 2) {
    //     // console.warn('* ', dis, vehicle.controlPoints, nextPoint.x, t);
    //     let tmp = vehicle.controlPoints.splice(0, 1)[0];
    //     vehicle.controlPoints.push(tmp);
    // }
    // console.log('* ', x, y, ' => ', nextPoint.x, nextPoint.y, ' -> ', dic, ', dis: ', dis);

    if (
        isNaN(dis) ||
        dis <= virtualVehiclesStep * 2 ||
        (vehicle.dicExtTmp === 1 && dic === 3) ||
        (vehicle.dicExtTmp === 0 && dic === 2)
    ) {
        let path = getNextPath(vehicle.routeExt);
        if (path) {
            // console.warn('[kernel] getVirtualPosition: ', path.id);
            vehicle.controlPoints = path.controlPoints;
            vehicle.routeExt = path;
            vehicle.route = [path.id];
            return { x, y, h };
        }
    }
    vehicle.dicExtTmp = dic;

    if (dic === 0) {
        y = vehicle.tunedPos.y + virtualVehiclesStep + (virtualVehiclesStep * 10) / (vehicle.idx + 1);
        h = 90;
    } else if (dic === 1) {
        x = vehicle.tunedPos.x + virtualVehiclesStep + (virtualVehiclesStep * 10) / (vehicle.idx + 1);
        h = 0;
    } else if (dic === 2) {
        y = vehicle.tunedPos.y - virtualVehiclesStep - (virtualVehiclesStep * 10) / (vehicle.idx + 1);
        h = 270;
    } else if (dic === 3) {
        x = vehicle.tunedPos.x - virtualVehiclesStep - (virtualVehiclesStep * 10) / (vehicle.idx + 1);
        h = 180;
    } else {
        x = 0;
        y = 0;
        h = 0;
        vehicle.controlPoints = null;
    }
    return { x, y, h };
}
let CLOTHOIDS = {}; //store every path clothoid points
function getVirtualPosition(vehicle) {
    //eslint-disable-line
    let x = vehicle.tunedPos.x,
        y = vehicle.tunedPos.y,
        h = 0;
    // if (vehicle.controlPoints.length === 2) {
    //     console.log('...> ', vehicle, vehicle.stepExt);
    //     return getNextPoint(vehicle);
    //     // if (vehicle.controlPoints.length === 2) return { x: 0, y: 0, h: 0 };
    // } else {
    if (!CLOTHOIDS[vehicle.routeExt.id])
        CLOTHOIDS[vehicle.routeExt.id] = Point.getClothoidPoints(vehicle.controlPoints, virtualVehiclesStep);
    vehicle.stepExt = vehicle.stepExt ? vehicle.stepExt + 1 : 1;
    // console.warn('[kernel] getVirtualPosition: ', CLOTHOIDS[vehicle.routeExt.id]);
    let p = CLOTHOIDS[vehicle.routeExt.id][vehicle.stepExt];
    if (!p) {
        vehicle.stepExt = 0;
        // if (!vehicle.historyExt) vehicle.historyExt = [];
        let path = getNextPath(vehicle.routeExt);
        if (!path) path = getRandomPath();
        // if (vehicle.historyExt.indexOf(path.id) >= 0 && vehicle.historyExt.length < resources.paths.length / 2)
        //     path = getRandomPath();
        if (path) {
            // console.warn('[kernel] getVirtualPosition: ', path.id);
            vehicle.controlPoints = path.controlPoints;
            vehicle.routeExt = path;
            vehicle.route = [path.id];
            // vehicle.historyExt.push(path.id);
        }
        // console.log(vehicle.id, '---> ', vehicle.historyExt.length);
        // vehicle.controlPoints.reverse();
        // CLOTHOIDS[vehicle.routeExt.id].reverse();
        // // console.warn('[kernel] getVirtualPosition: ', CLOTHOIDS[vehicle.routeExt.id], vehicle.stepExt);
        return getVirtualPosition(vehicle);
    }
    x = p.x;
    y = p.y;
    // let cp = new Point(vehicle.tunedPos.x, vehicle.tunedPos.y);
    // h = Math.round(cp.angle(p));
    let cp = new Point(p.x, p.y);
    h = Math.round(cp.angle(vehicle.tunedPos));
    // console.warn('[kernel] getVirtualPosition: ', cp, p, h);

    if (vehicle.routeExt && vehicle.routeExt.heading && vehicle.routeExt.heading === 'BACKWARD') {
        h += 180;
        // if (vehicle.idx === 0) console.warn('[kernel] getVirtualPosition: ', h);
    }
    // }
    return { x, y, h };
}
let POINT_LINKS = {}; // linked points
// Get a 'random' next path 
function getNextPath(path) {
    //eslint-disable-line
    let p = path.controlPoints[path.controlPoints.length - 1],
        po = new Point(p.x, p.y);
    // let s = resources['paths'].find(o => o.id !== path.id && po.equals(o.controlPoints[0]));
    let list = POINT_LINKS[path.id];
    if (!list || !list.length)
        list = POINT_LINKS[path.id] =
            resources['paths'].filter(o => o.id !== path.id && po.equals(o.controlPoints[0])) || []; //eslint-disable-line
    let idx = Math.floor(Math.random() * list.length);
    let s = list[idx];
    return s;
}
function virtualLifecycle() {
    let now = Date.now();
    lastServerTime = serverTime ? serverTime : now;
    lastMessageTime = now;
    serverTime = now;
    workTime = serverTime - lastServerTime;
    let ws = {
        timeDelta: workTime,
        workTime: workTime,
        serverClock: workTime,
        serverTime: serverTime
    };
    postMessage({ task: 'ws', data: { ws, d: Math.floor(Math.random() * 1000) } }); // 服务器时间状态

    resources['vehicles'].forEach((t, idx) => {
        /** follow the path's controlPoints ... */
        // let { x, y, h } = getNextPoint(t);
        let { x, y, h } = getVirtualPosition(t);
        t.tunedPos = { x, y, h };
        // Chef.vehicleUpdateProperty(t, 'tunedPos', { x, y, h });
        Chef.vehicleUpdateProperty(idx, 'tunedPos', { x, y, h });
    });

    // random alarm!
    if (isRandomStatusEnable && now - lastRandomTime > randomLimit) {
        resources['vehicles'].forEach((t, idx) => {
            let r = parseInt(Math.random() * 3);
            if (r === 0) {
                t.updateIndex = -1;
                t.isOnline = true;
            } else if (r === 1) {
                t.updateIndex = 0;
                t.isOnline = true;
            } else if (r === 2) {
                t.updateIndex = 1;
                t.isOnline = false;
            }
            t.properties.actionStatus = r;
            // if (t.properties.actionStatus === 2) delete t.properties.actionStatus;
            // Chef.vehicleUpdateProperty(t, 'properties', serverTime, workTime);
            Chef.vehicleUpdateProperty(idx, 'properties', serverTime, workTime);
            lastRandomTime = Date.now();
            // console.log('---> ', t.id, r, t.updateIndex, t.isOnline, t.properties.actionStatus);
            // if (idx === 0) console.log('---> ', t.id, r, t.updateIndex, t.isOnline, t.blinkerFillExt, t.blinkerRExt);
            // let idx = resources['vehicles'].findIndex(v => v.id === t.id);
            // resources[listName][idx][p] = list[i][p];
            // vehicleUpdateProperty(idx, 'tunedPos', { x, y, h });
            // console.log('---> ', t.id, t.updateIndex, t.isOnline, t.blinkerFillExt, t.blinkerRExt);
        });
    }

    // random scripts
    let rdm = Math.floor(Math.random() * 20);
    resources.scripts = [];
    for (let i = 10, len = rdm; i < len; i++) resources.scripts.push({ id: i, properties: { activation: 'true' } });
    // random events
    rdm = Math.floor(Math.random() * 10);
    resources.events = [];
    for (let i = 0, len = rdm; i < len; i++) resources.events.push({ id: i, properties: { isNow: 'true' } });
    // random jobs
    if (!resources.jobs || !resources.jobs.length) {
        rdm = Math.floor(Math.random() * 60);
        resources.jobs = [];
        for (let i = 30, len = rdm; i < len; i++) resources.jobs.push({ id: i, vehicleId: i });
    }

    refreshTypes = { vehicles: true, scripts: true, events: true, jobs: true };
    let rtn = {};
    refreshTypes = Object.assign(refreshTypes, reQueueTypes);
    Object.keys(resources).forEach(k => (rtn[k] = refreshTypes[k] ? resources[k] : null));
    // console.error(contoursi, rtn);
    postMessage({ task: 'increment', data: rtn, refreshTypes });
    reQueueTypes = {};
    virtualLifecycleTimer = setTimeout(virtualLifecycle, virtualLifecycleDelay);
}
function offlineQuickJob(res) {
    let idx = resources.vehicles.findIndex(v => v.id === res.vehicleKey),
        pth = resources.paths.find(v => v.id === res.orderList[0].path);
    let p = null;
    if (pth) p = pth.controlPoints[pth.controlPoints.length - 1];
    if (p) {
        // resources.vehicles[idx].tunedPos.x = p.x;
        // resources.vehicles[idx].tunedPos.y = p.y;
        let t = resources['vehicles'][idx];
        t.controlPoints = pth.controlPoints;
        t.routeExt = pth;
        t.route = [pth.id];
        resources['vehicles'][idx].stepExt = 1;
        let { x, y, h } = getVirtualPosition(t);
        // Chef.vehicleUpdateProperty(resources['vehicles'][idx], 'tunedPos', { x, y, h });
        Chef.vehicleUpdateProperty(idx, 'tunedPos', { x, y, h });
    }
    // console.warn('[offline] offlineQuickJob: ', res, idx, p);
}

// export default initLocalDate;
// export { initLocalDate as default, offlineQuickJob };
export { initLocalDate, offlineQuickJob };
